package edc.code.algorithms;

import edc.DataConverter;

/**
 *
 * @author vpopov
 */
public class HammingCodeAlgorithm extends CodeAlgorithm {

	private static HammingCodeAlgorithm instance;

	private HammingCodeAlgorithm() {
	}

	public static HammingCodeAlgorithm getInstance() {
		if (instance == null) {
			instance = new HammingCodeAlgorithm();
		}

		return instance;
	}

	public String encodeData(String data) {
		int parityBitsCount = calculateParityBitsCount(data);
		StringBuilder encodedDataBuilder = initializeEncodedDataBuilder(data, parityBitsCount);

		for (int i = 0; i < parityBitsCount; i++) {
			int onesCount = 0;
			int startPosition = (1 << i);

			for (int j = startPosition - 1; j < encodedDataBuilder.length(); j += 2 * startPosition) {
				for (int k = j; k < j + startPosition && k < encodedDataBuilder.length(); k++) {
					if (encodedDataBuilder.charAt(k) == '1') {
						onesCount++;
					}
				}
			}

			if (onesCount % 2 == 1) {
				encodedDataBuilder.setCharAt(startPosition - 1, '1');
			}
		}

		return encodedDataBuilder.toString();
	}

	private int calculateParityBitsCount(String data) {
		int parityBitsCount = 0;
		while (1 << parityBitsCount <= data.length()) {
			parityBitsCount++;
		}
		return parityBitsCount;
	}

	private StringBuilder initializeEncodedDataBuilder(String data, int parityBitsCount) {
		StringBuilder encodedDataBuilder = new StringBuilder();
		int encodedDataBuilderIndex = 0;
		for (int i = 0; i < data.length(); i++) {
			while ((1 << parityBitsCount) % (encodedDataBuilderIndex + 1) == 0) {
				encodedDataBuilder.append("0");
				encodedDataBuilderIndex++;
			}
			encodedDataBuilder.append(data.charAt(i));
			encodedDataBuilderIndex++;
		}
		return encodedDataBuilder;
	}
}
